package de.tabiteuea.gui;

import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.part.ViewPart;

import de.tabiteuea.gui.utils.ITableConstants;
import de.tabiteuea.gui.utils.ImageConstants;
import de.tabiteuea.gui.utils.ImageProvider;

/**
 * Contains all elements for presentation of the frequency analysis results.
 * <p>
 * It consists of a table for the word frequencies and a table for other
 * statistical results.
 * <p>
 * It is possible to to add entries from the frequency table to the word list at
 * the UI-View.
 * <p>
 * A special thanks to the women and men from http://www.java2s.com/.
 * 
 * @author Sebastian Lippert
 * 
 */

public class ResultView extends ViewPart {
	public static final String ID = "de.tabiteuea.gui.view1"; //$NON-NLS-1$

	private TableViewer freqViewer, statisticViewer;
	private Table freqTable, statisticTable;
	private Composite mainComp;

	/**
	 * Callback to create and initialize the viewer.
	 * 
	 */

	public void createPartControl(Composite parent) {

		FormToolkit toolkit = new FormToolkit(parent.getDisplay());

		mainComp = toolkit.createComposite(parent);
		mainComp.setLayout(new GridLayout(2, true));
		mainComp.setLayoutData(new GridData(GridData.FILL_BOTH));

		createFreqTable(mainComp, toolkit);
		createStatistics(mainComp, toolkit);

		mainComp.setVisible(false);

	}

	/*
	 * Creates right part of the view that contains the statistical table.
	 */

	private void createStatistics(Composite parent, FormToolkit toolkit) {

		Section coocSection;

		coocSection = toolkit.createSection(parent, Section.DESCRIPTION
				| Section.TITLE_BAR);
		coocSection.setText(Messages.ResultView_corpus_stat_sec);
		coocSection.setDescription(Messages.ResultView_corpus_stat_desc);
		coocSection.setLayout(new GridLayout());
		coocSection.setLayoutData(new GridData(GridData.FILL_BOTH));
		coocSection.clientVerticalSpacing = 10;

		Composite comp = toolkit.createComposite(coocSection);
		comp.setLayout(new GridLayout());
		comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		statisticViewer = new TableViewer(comp, SWT.BORDER | SWT.SINGLE);
		// Set the content and label providers
		statisticViewer.setContentProvider(new StatisticContentProvider());
		statisticViewer.setLabelProvider(new StatisticLabelProvider());
		statisticViewer.setSorter(new StatisticViewerSorter());

		statisticTable = statisticViewer.getTable();
		statisticTable.setLayout(new FillLayout(SWT.HORIZONTAL));
		statisticTable.setLayoutData(new GridData(GridData.FILL_BOTH));

		// add the parameter column
		TableColumn tc = new TableColumn(statisticTable, SWT.LEFT);
		tc.setText(Messages.ResultView_col_stat_param);
		tc.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				((StatisticViewerSorter) statisticViewer.getSorter())
						.doSort(ITableConstants.COLUMN_KEY);
				statisticViewer.refresh();
			}
		});
		// add the value column
		tc = new TableColumn(statisticTable, SWT.LEFT);
		tc.setText(Messages.ResultView_col_value);
		tc.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				((StatisticViewerSorter) statisticViewer.getSorter())
						.doSort(ITableConstants.COLUMN_VALUE);
				statisticViewer.refresh();
			}
		});

		statisticTable.update();
		statisticViewer.refresh();

		// turn on the header and the lines
		statisticTable.setHeaderVisible(true);
		statisticTable.setLinesVisible(true);

		// button to export table as csv
		Button export = new Button(comp, SWT.PUSH);
		export.setImage(ImageProvider.getImage(ImageConstants.EXPORT));
		export.setToolTipText(Messages.ResultView_export);
		export.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {

				// create head with names of the columns
				StringBuffer buffer = new StringBuffer();
				buffer.append(Messages.ResultView_col_stat_param + ",");
				buffer.append(Messages.ResultView_col_value
						+ System.getProperty("line.separator"));

				// add parameter and values
				@SuppressWarnings("unchecked")
				List<String[]> input = (List<String[]>) statisticViewer
						.getInput();
				for (String s[] : input) {

					buffer.append(s[0] + ",");
					buffer.append(s[1] + System.getProperty("line.separator"));

				}

				persistTable(buffer);
			}

		});

		coocSection.setClient(comp);
	}

	/*
	 * Creates left part of the view that contains the frequency table.
	 */

	private void createFreqTable(Composite parent, FormToolkit toolkit) {

		Section freqSection;

		freqSection = toolkit.createSection(parent, Section.DESCRIPTION
				| Section.TITLE_BAR);
		freqSection.setText(Messages.ResultView_freq_sec);
		freqSection.setDescription(Messages.ResultView_freq_desc);
		freqSection.setLayout(new GridLayout());
		freqSection.setLayoutData(new GridData(GridData.FILL_BOTH));
		freqSection.clientVerticalSpacing = 10;

		Composite comp = toolkit.createComposite(freqSection);
		comp.setLayout(new GridLayout());
		comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		// create the table viewer to display the results
		freqViewer = new TableViewer(comp, SWT.BORDER | SWT.SINGLE);
		// set the content and label providers
		freqViewer.setContentProvider(new FreqContentProvider());
		freqViewer.setLabelProvider(new FreqLabelProvider());
		freqViewer.setSorter(new FreqViewerSorter());

		// set up the table
		freqTable = freqViewer.getTable();
		freqTable.setLayout(new FillLayout(SWT.HORIZONTAL));
		freqTable.setLayoutData(new GridData(GridData.FILL_BOTH));

		// add the word column
		TableColumn tc = new TableColumn(freqTable, SWT.LEFT);
		tc.setText(Messages.ResultView_col_word);
		tc.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				((FreqViewerSorter) freqViewer.getSorter())
						.doSort(ITableConstants.COLUMN_WORD);
				freqViewer.refresh();
			}
		});
		// add the absolute frequency column
		tc = new TableColumn(freqTable, SWT.LEFT);
		tc.setText(Messages.ResultView_col_abs_freq);
		tc.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				((FreqViewerSorter) freqViewer.getSorter())
						.doSort(ITableConstants.COLUMN_ABSOLUTE_FREQ);
				freqViewer.refresh();
			}
		});

		// add the relative frequency column
		tc = new TableColumn(freqTable, SWT.LEFT);
		tc.setText(Messages.ResultView_col_rel_freq);
		tc.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				((FreqViewerSorter) freqViewer.getSorter())
						.doSort(ITableConstants.COLUMN_RELATIVE_FREQ);
				freqViewer.refresh();
			}
		});

		// add drag and drop support
		freqViewer.addDragSupport(DND.DROP_MOVE,
				new Transfer[] { TextTransfer.getInstance() },
				new DragSourceAdapter() {

					public void dragSetData(DragSourceEvent event) {

						IStructuredSelection sel = (IStructuredSelection) freqViewer
								.getSelection();
						if (sel.getFirstElement() instanceof String[]) {
							String[] selection = (String[]) sel
									.getFirstElement();
							event.data = selection[0];
						}

					}
				});

		freqTable.update();
		freqViewer.refresh();

		// turn on the header and the lines
		freqTable.setHeaderVisible(true);
		freqTable.setLinesVisible(true);

		// button to export table as csv
		Button export = new Button(comp, SWT.PUSH);
		export.setImage(ImageProvider.getImage(ImageConstants.EXPORT));
		export.setToolTipText(Messages.ResultView_export);
		export.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {

				// create head with names of the columns
				StringBuffer buffer = new StringBuffer();
				buffer.append(Messages.ResultView_col_word + ",");
				buffer.append(Messages.ResultView_col_abs_freq + ",");
				buffer.append(Messages.ResultView_col_rel_freq
						+ System.getProperty("line.separator"));

				// add words and their respective frequencies
				@SuppressWarnings("unchecked")
				List<String[]> input = (List<String[]>) freqViewer.getInput();
				for (String s[] : input) {

					buffer.append(s[0] + ",");
					buffer.append(s[1] + ",");
					buffer.append(s[2] + System.getProperty("line.separator"));

				}

				persistTable(buffer);

			}

		});

		freqSection.setClient(comp);
	}

	public void setInput(List<String[]> freqInput, List<String[]> statisticInput) {

		freqViewer.setInput(freqInput);
		for (TableColumn tc : freqTable.getColumns()) {
			tc.pack();
		}
		freqViewer.refresh();

		statisticViewer.setInput(statisticInput);
		for (TableColumn tc : statisticTable.getColumns()) {
			tc.pack();
		}
		statisticViewer.refresh();
		mainComp.setVisible(true);
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		// not in use
	}

	// ViewerSorter for frequency table
	private class FreqViewerSorter extends ViewerSorter {

		private static final int ASCENDING = 0;
		private static final int DESCENDING = 1;
		private int column;
		private int direction;

		/**
		 * Does the sorting. If the column is different from the previous sort,
		 * sort ascending. If it's the same column as the last sort, toggle sort
		 * direction.
		 * 
		 * @param column
		 *            Column to be sorted.
		 */
		public void doSort(int column) {
			if (column == this.column) {
				// Same column as last sort; toggle the direction
				direction = 1 - direction;
			} else {
				// New column; do an ascending sort
				this.column = column;
				direction = ASCENDING;
			}
		}

		/**
		 * Compares the object for sorting
		 * 
		 * @bug Crashes for words with a frequency larger than 2.147.483.647.
		 */
		public int compare(Viewer viewer, Object e1, Object e2) {
			int rc = 0;
			String[] p1 = (String[]) e1;
			String[] p2 = (String[]) e2;

			// Determine which column and do the appropriate sort
			switch (column) {
			case ITableConstants.COLUMN_WORD:
				rc = p1[0].compareTo(p2[0]);
				break;
			case ITableConstants.COLUMN_ABSOLUTE_FREQ:

				// words with a frequency larger than 2.147.483.647 crashes
				// tabiteuea
				int i1 = Integer.parseInt(p1[1]);
				int i2 = Integer.parseInt(p2[1]);

				if (i1 < i2)
					rc = -1;
				else if (i1 > i2)
					rc = 1;
				else
					rc = 0;
				break;

			case ITableConstants.COLUMN_RELATIVE_FREQ:

				float d1 = Float.parseFloat(p1[2]);
				float d2 = Float.parseFloat(p2[2]);

				if (d1 < d2)
					rc = -1;
				else if (d1 > d2)
					rc = 1;
				else
					rc = 0;

				break;
			}

			// If descending order, flip the direction
			if (direction == DESCENDING)
				rc = -rc;

			return rc;
		}
	}

	// Provides label texts for frequency table
	private class FreqLabelProvider implements ITableLabelProvider {

		public Image getColumnImage(Object arg0, int arg1) {
			return null;
		}

		/**
		 * Gets the text for the specified column.
		 * 
		 * @param arg0
		 *            the row
		 * @param arg1
		 *            the column
		 * @return String desired text
		 */
		public String getColumnText(Object arg0, int arg1) {

			String[] countArray = (String[]) arg0;
			String text = ""; //$NON-NLS-1$
			switch (arg1) {
			case ITableConstants.COLUMN_WORD:
				text = countArray[0];
				break;
			case ITableConstants.COLUMN_ABSOLUTE_FREQ:
				text = countArray[1];
				break;
			case ITableConstants.COLUMN_RELATIVE_FREQ:
				text = countArray[2];
				break;
			}
			return text;
		}

		/**
		 * Adds a listener
		 * 
		 * @param arg0
		 *            the listener
		 */
		public void addListener(ILabelProviderListener arg0) {
			// Throw it away
		}

		/**
		 * Dispose any created resources
		 */
		public void dispose() {
		}

		/**
		 * Returns whether the specified property, if changed, would affect the
		 * label
		 * 
		 * @param arg0
		 *            the row
		 * @param arg1
		 *            the property
		 * @return boolean Affects the label? (Always <code>false</code>)
		 */
		public boolean isLabelProperty(Object arg0, String arg1) {
			return false;
		}

		/**
		 * Removes the specified listener
		 * 
		 * @param arg0
		 *            the listener to be removed
		 */
		public void removeListener(ILabelProviderListener arg0) {
			// Do nothing
		}
	}

	// ContentProvider for frequency table
	private class FreqContentProvider implements IStructuredContentProvider {

		/**
		 * Gets the elements for the table
		 * 
		 * @param arg0
		 *            the model
		 * @return Object[] table item
		 */
		@SuppressWarnings("unchecked")
		public Object[] getElements(Object arg0) {
			return ((List<String>) arg0).toArray();
		}

		/**
		 * Disposes any resources
		 */
		public void dispose() {
			// We don't create any resources, so we don't dispose any
		}

		/**
		 * Is called when the input changes
		 * 
		 * @param arg0
		 *            the parent viewer
		 * @param arg1
		 *            the old input
		 * @param arg2
		 *            the new input
		 */
		public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
			// Nothing to do
		}
	}

	// ViewerSorter for statistic Table
	private class StatisticViewerSorter extends ViewerSorter {

		private static final int ASCENDING = 0;
		private static final int DESCENDING = 1;
		private int column;
		private int direction;

		/**
		 * Does the sorting. If the column is different from the previous sort,
		 * sort ascending. If it's the same column as the last sort, toggle sort
		 * direction.
		 * 
		 * @param column
		 *            to be sorted
		 */
		public void doSort(int column) {
			if (column == this.column) {
				// Same column as last sort; toggle the direction
				direction = 1 - direction;
			} else {
				// New column; do an ascending sort
				this.column = column;
				direction = ASCENDING;
			}
		}

		/**
		 * Compares the object for sorting
		 */
		public int compare(Viewer viewer, Object e1, Object e2) {
			int rc = 0;
			String[] p1 = (String[]) e1;
			String[] p2 = (String[]) e2;

			// Determine which column and do the appropriate sort
			switch (column) {
			case ITableConstants.COLUMN_KEY:
				rc = p1[0].compareTo(p2[0]);
				break;
			case ITableConstants.COLUMN_VALUE:

				float i1 = Float.parseFloat(p1[1]);
				float i2 = Float.parseFloat(p2[1]);

				if (i1 < i2)
					rc = -1;
				else if (i1 > i2)
					rc = 1;
				else
					rc = 0;
				break;
			}
			// If descending order, flip the direction
			if (direction == DESCENDING)
				rc = -rc;

			return rc;
		}
	}

	// LabelProvider for statistic Table
	private class StatisticLabelProvider implements ITableLabelProvider {

		public Image getColumnImage(Object arg0, int arg1) {
			return null;
		}

		/**
		 * Gets the text for the specified column
		 * 
		 * @param arg0
		 *            the row
		 * @param arg1
		 *            the column
		 * @return String the desired text
		 */
		public String getColumnText(Object arg0, int arg1) {

			String[] countArray = (String[]) arg0;
			String text = ""; //$NON-NLS-1$
			switch (arg1) {
			case ITableConstants.COLUMN_KEY:
				text = countArray[0];
				break;
			case ITableConstants.COLUMN_VALUE:
				text = countArray[1];
				break;
			}
			return text;
		}

		/**
		 * Adds a listener
		 * 
		 * @param arg0
		 *            the listener
		 */
		public void addListener(ILabelProviderListener arg0) {
			// Throw it away
		}

		/**
		 * Dispose any created resources
		 */
		public void dispose() {
		}

		/**
		 * Returns whether the specified property, if changed, would affect the
		 * label
		 * 
		 * @param arg0
		 *            the row
		 * @param arg1
		 *            the property
		 * @return boolean Affects the label? (Always <code>false</code>)
		 */
		public boolean isLabelProperty(Object arg0, String arg1) {
			return false;
		}

		/**
		 * Removes the specified listener
		 * 
		 * @param arg0
		 *            the listener
		 */
		public void removeListener(ILabelProviderListener arg0) {
			// Do nothing
		}
	}

	// ContentProvider for statistic Table
	private class StatisticContentProvider implements
			IStructuredContentProvider {

		/**
		 * Gets the elements for the table
		 * 
		 * @param arg0
		 *            the model
		 * @return Object[] table item
		 */
		@SuppressWarnings("unchecked")
		public Object[] getElements(Object arg0) {
			return ((List<String>) arg0).toArray();
		}

		/**
		 * Disposes any resources
		 */
		public void dispose() {
			// We don't create any resources, so we don't dispose any
		}

		/**
		 * Called when the input changes
		 * 
		 * @param arg0
		 *            the parent viewer
		 * @param arg1
		 *            the old input
		 * @param arg2
		 *            the new input
		 */
		public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
			// Nothing to do
		}
	}

	/*
	 * Export table to csv-file.
	 * 
	 * @param buffer Table content to write
	 */

	private void persistTable(StringBuffer buffer) {

		FileDialog fileDialog = new FileDialog(mainComp.getShell(), SWT.SAVE);
		fileDialog.setText(Messages.ResultView_save);
		fileDialog.setFilterExtensions(new String[] { "*.csv" });

		String savePath = fileDialog.open();
		if (savePath == null || savePath.isEmpty())
			return;

		try {

			FileWriter writer = new FileWriter(savePath);
			writer.write(buffer.toString());
			writer.close();

		} catch (IOException e) {
			MessageDialog.openError(
					mainComp.getShell(),
					Messages.ResultView_export_error,
					Messages.ResultView_export_error_desc + " "
							+ e.getLocalizedMessage());
			persistTable(buffer);
		}

	}

}
